﻿using System;
using System.Collections.Generic;
using System.Linq;
using Newtonsoft.Json;
using T.FAS.Runtime.DataModel.Dao;
using T.FAS.Runtime.Metadata.Entity.DataModel;
using T.FAS.Runtime.Metadata.SPI;
using T.FAS.Runtime.Metadata.Entity;
using T.FAS.Runtime.Base.Entity;
using System.Dynamic;

namespace T.FAS.Runtime.DataModel.Plugin
{
    /// <summary>
    /// DO元数据同步管理
    /// </summary>
    public class DataObjectMetaEntityManager : IMetaDataSyncManager
    {
        private readonly DatabaseLogic databaseLogic = new DatabaseLogic();
        private readonly Dictionary<DatabaseType, IMetaBusinessSQLBuilder> sqlBuildFactoryDict = new Dictionary<DatabaseType, IMetaBusinessSQLBuilder>();

        /// <summary>
        /// 元数据同步
        /// </summary>
        /// <param name="contents">元数据集合</param>
        public void Sync(IList<IMetaDataContent> contents)
        {
            List<SQLItem> sqls = new List<SQLItem>();
            List<SQLItem> presetDataSQLs = new List<SQLItem>();
            DataObjectSQLBuilderFactory fac = new DataObjectSQLBuilderFactory();
            //检测输入参数有效性
            foreach (var cnt in contents)
            {
                if (!(cnt is DataObject dataObject))
                {
                    throw new FasException(DataModelErrorCode.DO.SyncError, "DO同步表结构的DO实例不能为null!");
                }
                if (!dataObject.IsStatic && dataObject.DisableSyncStructure) continue;
                if (string.IsNullOrEmpty(dataObject.DatabaseID))
                {
                    throw new FasException(DataModelErrorCode.DO.SyncError, $"DO同步表结构的DO:{dataObject.Code},所属的DB信息不能为空!");
                }
                if (dataObject.Database == null)
                {
                    //尝试获取当前环境的DB
                    dataObject.Database = databaseLogic.GetDatabaseByID(dataObject.DatabaseID);
                    if (dataObject.Database == null)
                    {
                        throw new FasException(DataModelErrorCode.DO.SyncError, $"DO同步表结构的DO:{dataObject.Code},所属的DB{dataObject.DatabaseID}在当前环境找不到对应的实例！");
                    }
                    if (string.IsNullOrEmpty(dataObject.Database.DbType))
                    {
                        throw new FasException(DataModelErrorCode.DO.SyncError, $"DO同步表结构的DO:{dataObject.Code},所属的DB{dataObject.Code}数据库类型为空！");
                    }
                }
            }

            foreach (var cnt in contents)
            {
                DataObject dataObject = cnt as DataObject;
                if (!dataObject.IsStatic && dataObject.DisableSyncStructure) continue;
                //视图类DO不生成建表语句
                if (dataObject.IsView) continue;
                //根据DbType生成相应数据库的SQLBuilder
                var dbtype = (DatabaseType)Enum.Parse(typeof(DatabaseType), dataObject.Database.DbType);
                if (!sqlBuildFactoryDict.Keys.Contains(dbtype))
                {
                    sqlBuildFactoryDict[dbtype] = fac.GetBusinessSQLBuilder(dbtype);
                    sqlBuildFactoryDict[dbtype].Contents = contents;
                }
                //生成可执行的SQL语句
                sqls.AddRange(sqlBuildFactoryDict[dbtype].BuildBusinessSQL(dataObject));
                if (dataObject.IsStatic)
                    presetDataSQLs.AddRange(sqlBuildFactoryDict[dbtype].BuildPresetDataSQL(dataObject));
            }

            try
            {
                //DDL DML不支持事务控制，不启用事务
                //执行DDL
                SQLExcutor.ExcuteSQLs(sqls);
                //执行DML
                SQLExcutor.ExcuteSQLs(presetDataSQLs);
            }
            catch (Exception e)
            {
                throw new FasException(DataModelErrorCode.DO.SyncError, "DO结构同步失败或数据预置失败", e);
            }
        }


        /// <summary>
        /// 元数据同步调试用
        /// </summary>
        /// <param name="contents">元数据集合</param>
        public List<SQLItem> ExportSQL(IList<IMetaDataContent> contents)
        {
            List<SQLItem> sqls = new List<SQLItem>();
            List<SQLItem> presetDataSQLs = new List<SQLItem>();
            DataObjectSQLBuilderFactory fac = new DataObjectSQLBuilderFactory();
            //检测输入参数有效性
            foreach (var cnt in contents)
            {
                if (!(cnt is DataObject dataobject))
                {
                    throw new FasException(DataModelErrorCode.DO.SyncError, "DO同步表结构的DO实例不能为null!");
                }
                if (string.IsNullOrEmpty(dataobject.DatabaseID))
                {
                    throw new FasException(DataModelErrorCode.DO.SyncError, $"DO同步表结构的DO:{dataobject.Code},所属的DB信息不能为空!");
                }
                if (dataobject.Database == null)
                {
                    //尝试获取当前环境的DB
                    dataobject.Database = databaseLogic.GetDatabaseByID(dataobject.DatabaseID);
                    if (dataobject.Database == null)
                    {
                        throw new FasException(DataModelErrorCode.DO.SyncError, $"DO同步表结构的DO:{dataobject.Code},所属的DB{dataobject.DatabaseID}在当前环境找不到对应的实例！");
                    }
                    if (string.IsNullOrEmpty(dataobject.Database.DbType))
                    {
                        throw new FasException(DataModelErrorCode.DO.SyncError, $"DO同步表结构的DO:{dataobject.Code},所属的DB{dataobject.Code}数据库类型为空！");
                    }
                }
            }

            foreach (var cnt in contents)
            {
                var dataObject = cnt as DataObject;
                //视图类DO不生成建表语句
                if (dataObject.IsView) continue;
                //根据DbType生成相应数据库的SQLBuilder
                var dbtype = (DatabaseType)Enum.Parse(typeof(DatabaseType), dataObject.Database.DbType);
                if (!sqlBuildFactoryDict.Keys.Contains(dbtype))
                {
                    sqlBuildFactoryDict[dbtype] = fac.GetBusinessSQLBuilder(dbtype);
                    sqlBuildFactoryDict[dbtype].Contents = contents;
                }
                //生成可执行的SQL语句
                sqls.AddRange(sqlBuildFactoryDict[dbtype].BuildBusinessSQL(dataObject));
                if (dataObject.IsStatic)
                    presetDataSQLs.AddRange(sqlBuildFactoryDict[dbtype].BuildPresetDataSQL(dataObject));
            }
            sqls.AddRange(presetDataSQLs);
            return sqls;
        }
    }
}
